﻿using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;
using Nemerle.Assertions;
using System;
using System.Collections.Generic;
using System.Linq;
using Nemerle2.Compiler;
using Microsoft.Cci;
using Nemerle;

namespace MetadataReader.CCI
{
  /// <summary>
  /// Description of CciVariantTypeInfo.
  /// </summary>
  internal partial class CciVariantTypeInfo : CciTypeInfo, IVariantTypeInfo
  {
    private mutable _variantOptions    : array[IVariantOptionTypeInfo];
    private static VariantOptionEmpty  : array[IVariantOptionTypeInfo] = array(0);

    public this(provider : CciMetadataProvider, typeDef : INamedTypeDefinition, assembly : CciReferencedAssembly)
    {
      base(provider, typeDef, assembly);
    }

    /// <summary>
    /// Gets a value indicating whether the TypeInfo is a variant type
    /// </summary>
    public override IsVariant : bool { get { true } }

    /// <summary>
    /// Gets the options of the variant
    /// </summary>
    /// <value>
    /// A sequence of <c>NTypeInfo</c> objects representing the options of the type info if it's variant definition
    /// otherwise the value is empty sequence.
    /// </value>
    public VariantOptions : Seq[IVariantOptionTypeInfo]
    {
      get
      {
        TryInitializeVariantOptions();
        _variantOptions
      }
    }

    private TryInitializeVariantOptions() : void
    {
      def parseVariantOptions() : array[IVariantOptionTypeInfo]
      {
        if (IsVariant)
        {
          match (CustomAttributes.CustomAttribute[Constants.VariantAttributeFullName])
          {
            | [variantAttr] =>
              def nestedTypes = GetMembers().OfType.[ITypeInfo]().ToArray();

              def getVariantOption(name : string, index : int) : IVariantOptionTypeInfo
              {
                def opt = nestedTypes.OfType.[CciVariantOptionTypeInfo]().First(ti => ti.FullName == name);
                opt._variantOptionCode = index;
                opt
              }

              match (variantAttr.ArgumentValue[0])
              {
                | String(argValue) => argValue.Split(',').Select((name, index) => getVariantOption(name, index)).ToArray()
                | value => assert(false, $"VariantAttribute must has one positional argument of string type, but has '$value' (with $(value.GetType().Name) type).");
              }

            | [] | null     => VariantOptionEmpty
            | _ :: _        => assert(false, "The VariantAttribute specified more then one time.")
          }

        }
        else
          VariantOptionEmpty
      }

      when (_variantOptions == null)
        _variantOptions = parseVariantOptions();
    }
  }
}
